import sys
import os
import win32gui  # type: ignore
import win32con
import ctypes
import traceback
import threading
from PyQt5.QtWidgets import (
    QApplication, QPushButton, QMainWindow, QLabel, QCheckBox, QSlider,
    QVBoxLayout, QWidget, QFormLayout, QSizePolicy, QGraphicsOpacityEffect, QHBoxLayout
)
from PyQt5.QtCore import Qt, QRect, QSize, QObject, pyqtSignal
from PyQt5.QtGui import QPixmap, QIcon
from pynput import keyboard
from pynput.keyboard import Key, Listener
import json
from PyQt5.QtWidgets import QDialog, QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, QLineEdit
from PyQt5.QtCore import Qt, QTimer

# 创建全局信号用于线程安全更新UI
class KeySignals(QObject):
    key_pressed = pyqtSignal(str)
    key_released = pyqtSignal(str)

# 检查是否具有管理员权限
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except Exception as e:
        print(f"管理权限检查失败: {e}")
        return False

# 提升进程权限
def run_as_admin():
    if not is_admin():
        try:
            ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
            sys.exit(0)
        except Exception as e:
            print(f"提权失败: {e}")
            return False
    return True

# 资源文件夹路径
if getattr(sys, 'frozen', False):  # 如果是打包后的可执行文件
    RESOURCE_DIR = os.path.join(sys._MEIPASS, "resources")
else:
    RESOURCE_DIR = os.path.join(os.getcwd(), "resources")  # 开发环境下的路径

# 默认映射配置
DEFAULT_MAPPINGS = {
    "向前1": {"primary": "up", "secondary": None},
    "向前2": {"primary": "w", "secondary": None},
    "向后": {"primary": "down", "secondary": "s"},
    "向左": {"primary": "left", "secondary": "a"},
    "向右": {"primary": "right", "secondary": "d"},
    "漂移": {"primary": "shift", "secondary": "q"},
    "使用道具": {"primary": "ctrl", "secondary": "e"},
    "道具换位": {"primary": "alt", "secondary": None},
    "赛车复位": {"primary": "r", "secondary": None},
    "滑轮跳跃": {"primary": "space", "secondary": "x"},
    "跳跃键": {"primary": "f", "secondary": None}
}

# 功能键到图片资源的映射
FUNCTION_TO_KEY = {
    "向前1": "up",
    "向前2": "w",
    "向后": "down",
    "向左": "left",
    "向右": "right",
    "漂移": "shift",
    "使用道具": "ctrl",
    "道具换位": "alt",
    "赛车复位": "r",
    "滑轮跳跃": "space",
    "跳跃键": "f"
}



class KeyMappingDialog(QDialog):
    def __init__(self, mappings, parent=None):
        super().__init__(parent)
        self.setWindowTitle("按键映射配置")
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.resize(500, 375)
        
        self.mappings = mappings
        self.current_editing = None
        self.conflict_handling = {}
        
        # 初始化修饰键状态变量
        self.ctrl_pressed = False
        self.shift_pressed = False
        self.alt_pressed = False
        self.capturing = False
        
        layout = QVBoxLayout()
        
        # 创建表格
        self.table = QTableWidget(len(self.mappings), 3)
        self.table.setHorizontalHeaderLabels(["功能", "第一按键", "第二按键"])
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        
        # 连接单元格点击事件
        self.table.cellClicked.connect(self.start_key_capture)
        
        # 填充表格数据
        functions = list(self.mappings.keys())
        for row, func in enumerate(functions):
            # 功能名称
            func_item = QTableWidgetItem(func)
            self.table.setItem(row, 0, func_item)
            
            # 第一按键
            self.table.setItem(row, 1, self.create_key_item(self.mappings[func]["primary"]))
            
            # 第二按键
            self.table.setItem(row, 2, self.create_key_item(self.mappings[func]["secondary"]))
        
        layout.addWidget(self.table)
        self.setLayout(layout)
    
    def create_key_item(self, key):
        item = QTableWidgetItem(key.upper() if key else "无")
        if not key:
            item.setForeground(Qt.gray)
        return item
    
    def start_key_capture(self, row, column):
        """当用户点击单元格时开始按键捕获"""
        # 只处理按键列（第1列和第2列）
        if column not in (1, 2):
            return
            
        # 如果正在捕获，先完成之前的捕获
        if self.capturing:
            self.finalize_editing()
            
        func = self.table.item(row, 0).text()
        key_type = "primary" if column == 1 else "secondary"
        
        self.current_row = row
        self.current_column = column
        self.key_type_to_capture = key_type
        self.func_to_capture = func
        
        # 设置标识准备捕获按键
        self.capturing = True
        
        # 直接在单元格中显示提示
        prompt_label = QLabel("请按新按键")
        prompt_label.setAlignment(Qt.AlignCenter)
        prompt_label.setStyleSheet(
            "background-color: #E1F5FE;"  # 浅蓝色背景
            "font-weight: bold;"          # 加粗字体
            "color: #0288D1;"              # 蓝色字体
            "border: 1px solid #B3E5FC;"   # 边框
        )
        self.table.setCellWidget(row, column, prompt_label)
        
        # 确保对话框获得焦点
        self.setFocus()
    
    def keyPressEvent(self, event):
        if not self.capturing:
            return super().keyPressEvent(event)
        
        # 获取按下的键
        key_name = self.get_key_name(event.key(), event.modifiers())
        
        # 允许ESC按键取消捕获
        if event.key() == Qt.Key_Escape:
            # ESC取消捕获，不再设置新键
            self.finalize_editing()
            return
        
        # 处理特殊修饰键
        if event.key() in (Qt.Key_Control, Qt.Key_Shift, Qt.Key_Alt, Qt.Key_Meta):
            # 只设置修饰键状态，不处理为有效按键
            if event.key() == Qt.Key_Control:
                self.ctrl_pressed = True
            elif event.key() == Qt.Key_Shift:
                self.shift_pressed = True
            elif event.key() == Qt.Key_Alt:
                self.alt_pressed = True
                
            # 更新提示文本
            prompt = "按键: "
            if self.ctrl_pressed:
                prompt += "Ctrl+"
            if self.shift_pressed:
                prompt += "Shift+"
            if self.alt_pressed:
                prompt += "Alt+"
            
            # 移除单元格部件并更新提示
            self.table.setCellWidget(self.current_row, self.current_column, None)
            
            prompt_label = QLabel(prompt + "等待结束")
            prompt_label.setAlignment(Qt.AlignCenter)
            prompt_label.setStyleSheet(
                "background-color: #FFF9C4;"  # 浅黄色背景
                "font-weight: bold;"
                "color: #F57F17;"             # 橙色字体
                "border: 1px solid #FFEE58;"  # 边框
            )
            self.table.setCellWidget(self.current_row, self.current_column, prompt_label)
            return
            
        # 如果有有效键名
        if key_name:
            # 重置修饰键状态
            self.ctrl_pressed = False
            self.shift_pressed = False
            self.alt_pressed = False
            
            # 处理键值冲突
            conflict = self.check_key_conflict(key_name)
            
            if conflict:
                # 从冲突功能中移除按键
                conflict_func = conflict["func"]
                conflict_type = conflict["key"]
                
                # 更新冲突功能的映射
                self.mappings[conflict_func][conflict_type] = None
                
                # 更新表格显示
                for row in range(self.table.rowCount()):
                    if self.table.item(row, 0).text() == conflict_func:
                        if conflict_type == "primary":
                            self.table.setItem(row, 1, self.create_key_item(None))
                        else:
                            self.table.setItem(row, 2, self.create_key_item(None))
                        break
            
            # 应用新键值
            self.apply_new_key(key_name)
        else:
            # 无法识别的键，恢复原始内容
            self.finalize_editing()

    def get_key_name(self, key, modifiers):
        """改进按键识别逻辑，支持更多按键"""
        # 创建扩展的键码映射
        key_map = {
            Qt.Key_Up: "up",
            Qt.Key_Down: "down",
            Qt.Key_Left: "left",
            Qt.Key_Right: "right",
            Qt.Key_Space: "space",
            Qt.Key_Tab: "tab",
            Qt.Key_Backspace: "backspace",
            Qt.Key_Delete: "delete",
            Qt.Key_Insert: "insert",
            Qt.Key_Home: "home",
            Qt.Key_End: "end",
            Qt.Key_PageUp: "pageup",
            Qt.Key_PageDown: "pagedown",
            Qt.Key_Enter: "enter",
            Qt.Key_Return: "enter",  # 主键盘Enter
            Qt.Key_Print: "print",
            Qt.Key_ScrollLock: "scrolllock",
            Qt.Key_Pause: "pause",
            Qt.Key_NumLock: "numlock",
            Qt.Key_Escape: "escape",
            Qt.Key_CapsLock: "capslock",
            
            # 特殊键
            Qt.Key_Control: "ctrl",
            Qt.Key_Shift: "shift",
            Qt.Key_Alt: "alt",
            Qt.Key_Meta: "win",
            
            # 数字键 (0-9)
            Qt.Key_0: "0", Qt.Key_1: "1", Qt.Key_2: "2", Qt.Key_3: "3", Qt.Key_4: "4",
            Qt.Key_5: "5", Qt.Key_6: "6", Qt.Key_7: "7", Qt.Key_8: "8", Qt.Key_9: "9",
            
            # 字母键 (A-Z)
            Qt.Key_A: "a", Qt.Key_B: "b", Qt.Key_C: "c", Qt.Key_D: "d", Qt.Key_E: "e",
            Qt.Key_F: "f", Qt.Key_G: "g", Qt.Key_H: "h", Qt.Key_I: "i", Qt.Key_J: "j",
            Qt.Key_K: "k", Qt.Key_L: "l", Qt.Key_M: "m", Qt.Key_N: "n", Qt.Key_O: "o",
            Qt.Key_P: "p", Qt.Key_Q: "q", Qt.Key_R: "r", Qt.Key_S: "s", Qt.Key_T: "t",
            Qt.Key_U: "u", Qt.Key_V: "v", Qt.Key_W: "w", Qt.Key_X: "x", Qt.Key_Y: "y",
            Qt.Key_Z: "z",
            
            # 功能键 F1-F12
            Qt.Key_F1: "f1", Qt.Key_F2: "f2", Qt.Key_F3: "f3", Qt.Key_F4: "f4", 
            Qt.Key_F5: "f5", Qt.Key_F6: "f6", Qt.Key_F7: "f7", Qt.Key_F8: "f8", 
            Qt.Key_F9: "f9", Qt.Key_F10: "f10", Qt.Key_F11: "f11", Qt.Key_F12: "f12",
            
            # 符号键
            Qt.Key_Exclam: "!",          # !
            Qt.Key_At: "@",              # @
            Qt.Key_NumberSign: "#",      # #
            Qt.Key_Dollar: "$",          # $
            Qt.Key_Percent: "%",         # %
            Qt.Key_AsciiCircum: "^",     # ^
            Qt.Key_Ampersand: "&",       # &
            Qt.Key_Asterisk: "*",        # *
            Qt.Key_ParenLeft: "(",       # (
            Qt.Key_ParenRight: ")",      # )
            Qt.Key_Plus: "+",            # +
            Qt.Key_Minus: "-",           # -
            Qt.Key_Underscore: "_",      # _
            Qt.Key_Equal: "=",           # =
            Qt.Key_BraceLeft: "{",       # {
            Qt.Key_Bar: "|",             # |
            Qt.Key_BraceRight: "}",      # }
            Qt.Key_Colon: ":",           # :
            Qt.Key_QuoteDbl: '"',        # "
            Qt.Key_Less: "<",            # <
            Qt.Key_Greater: ">",         # >
            Qt.Key_Question: "?",        # ?
            Qt.Key_AsciiTilde: "~",      # ~
            Qt.Key_QuoteLeft: "`",       # `
            Qt.Key_BracketLeft: "[",     # [
            Qt.Key_Backslash: "\\",      # \
            Qt.Key_BracketRight: "]",    # ]
            Qt.Key_Semicolon: ";",       # ;
            Qt.Key_Apostrophe: "'",      # '
            Qt.Key_Comma: ",",           # ,
            Qt.Key_Period: ".",          # .
            Qt.Key_Slash: "/",            # /
        }
        
        # 处理已知特殊按键
        if key in key_map:
            return key_map[key]
        
        # 处理小键盘按键
        if key == Qt.Key_Keypad0: return "numpad0"
        if key == Qt.Key_Keypad1: return "numpad1"
        if key == Qt.Key_Keypad2: return "numpad2"
        if key == Qt.Key_Keypad3: return "numpad3"
        if key == Qt.Key_Keypad4: return "numpad4"
        if key == Qt.Key_Keypad5: return "numpad5"
        if key == Qt.Key_Keypad6: return "numpad6"
        if key == Qt.Key_Keypad7: return "numpad7"
        if key == Qt.Key_Keypad8: return "numpad8"
        if key == Qt.Key_Keypad9: return "numpad9"
        if key == Qt.Key_KeypadDecimal: return "numpad_decimal"
        if key == Qt.Key_KeypadDivide: return "numpad_divide"
        if key == Qt.Key_KeypadMultiply: return "numpad_multiply"
        if key == Qt.Key_KeypadMinus: return "numpad_subtract"
        if key == Qt.Key_KeypadPlus: return "numpad_add"
        if key == Qt.Key_KeypadEnter: return "numpad_enter"

        return None

    def check_key_conflict(self, new_key):
        """检查新按键是否已被其他功能使用"""
        # 跳过当前功能
        for func, mapping in self.mappings.items():
            if func == self.func_to_capture:
                continue
                
            if mapping["primary"] == new_key:
                return {"func": func, "key": "primary"}
                
            if mapping["secondary"] == new_key:
                return {"func": func, "key": "secondary"}
        
        return None
    
    def apply_new_key(self, key_name):
        # 更新映射字典
        self.mappings[self.func_to_capture][self.key_type_to_capture] = key_name.lower()
        
        # 更新表格显示
        new_item = self.create_key_item(key_name)
        self.table.setItem(self.current_row, self.current_column, new_item)
        
        # 重置捕获状态
        self.finalize_editing()
    
    def finalize_editing(self):
    # 保存配置
        self.parent().save_key_mapping()
        # 重置修饰键状态
        self.ctrl_pressed = False
        self.shift_pressed = False
        self.alt_pressed = False
        
        # 恢复正常捕获状态
        self.capturing = False
        
        # 移除单元格部件
        self.table.setCellWidget(self.current_row, self.current_column, None)



# 全局变量
key_states = {
    'q': False,
    'w': False,
    'e': False,
    'r': False,
    'up': False,
    'down': False,
    'left': False,
    'right': False,
    'shift': False,
    'ctrl': False,
    'alt': False,
    'f': False,
    'space': False,
}

# 初始按键位置（有间隔） - 注意：alt、f、space的位置将在初始化时动态计算
original_key_positions = { 
    'w': QRect(140, 220, 50, 50),
    'r': QRect(260, 220, 50, 50),
    'shift': QRect(10, 220, 100, 50),
    'ctrl': QRect(10, 280, 100, 50),
    'up': QRect(200, 220, 50, 50),
    'down': QRect(200, 280, 50, 50),
    'left': QRect(140, 280, 50, 50),
    'right': QRect(260, 280, 50, 50),
    # 底部行位置将在浮动窗口初始化时动态设置
    'alt': QRect(0, 340, 100, 50),  # 暂设默认位置，会被覆盖
    'f': QRect(0, 340, 50, 50),     # 暂设默认位置，会被覆盖
    'space': QRect(0, 340, 200, 50) # 暂设默认位置，会被覆盖
}

# 紧凑按键位置（无间隔） - 同样，底部行位置将动态计算
compact_key_positions = { 
    'w': QRect(100, 220, 50, 50),
    'r': QRect(180, 220, 50, 50),
    'shift': QRect(0, 220, 100, 50),
    'ctrl': QRect(0, 260, 100, 50),
    'up': QRect(140, 220, 50, 50),
    'down': QRect(140, 260, 50, 50),
    'left': QRect(100, 260, 50, 50),
    'right': QRect(180, 260, 50, 50),
    # 底部行位置将在浮动窗口初始化时动态设置
    'alt': QRect(0, 300, 100, 50),  # 暂设默认位置，会被覆盖
    'f': QRect(0, 300, 50, 50),     # 暂设默认位置，会被覆盖
    'space': QRect(0, 300, 180, 50) # 暂设默认位置，会被覆盖
}

# 初始使用原始布局
key_positions = original_key_positions.copy()
bot = False  # 缩放状态标志
compact_window_size = QSize(350, 350)  # 紧凑布局的窗口大小
original_window_size = QSize(440, 400)  # 原始布局的窗口大小
key_labels = {}  # 按键标签字典

# 创建全局信号
key_signals = KeySignals()

# 加载图片（增加错误处理）
def load_image(key, pressed=False):
    try:
        if pressed:
            image_path = os.path.join(RESOURCE_DIR, f"{key}_2.png")  # 按下状态图片
        else:
            image_path = os.path.join(RESOURCE_DIR, f"{key}.png")  # 未按下状态图片
            
        if not os.path.exists(image_path):
            print(f"警告: 图片文件 '{image_path}' 不存在")
            return QPixmap(50, 50)  # 返回空图片
        
        pixmap = QPixmap(image_path)
        return pixmap
    except Exception as e:
        print(f"加载图片出错: {e}")
        return QPixmap(50, 50)  # 返回空图片

# 浮动窗口
class FloatingWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowFlags(
            Qt.FramelessWindowHint | 
            Qt.WindowStaysOnTopHint | 
            Qt.Tool  # 重要：避免窗口获取焦点
        )
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setGeometry(100, 100, 440, 440)
        self.init_ui()
        
        # 连接信号
        key_signals.key_pressed.connect(self.on_key_pressed)
        key_signals.key_released.connect(self.on_key_released)

    def init_ui(self):
        # 使用全局key_positions初始化
        for key, rect in key_positions.items():
            label = QLabel(self)
            label.setGeometry(rect)
            label.setScaledContents(True)
            pixmap = load_image(key, False)
            label.setPixmap(pixmap)
            key_labels[key] = label

        # 创建框架标签 - 但不要让它拦截鼠标事件
        self.frame_label = QLabel(self)
        self.frame_label.setGeometry(0, 0, self.width(), self.height())
        self.frame_label.setStyleSheet("border: 2px dashed red;")
        self.frame_label.setAttribute(Qt.WA_TransparentForMouseEvents)  # 关键修复 - 让鼠标事件穿透
        self.frame_label.hide()
        
        # 初始状态下隐藏这三个按键（默认不显示）
        key_labels['alt'].hide()
        key_labels['f'].hide()
        key_labels['space'].hide()

        # 创建框架标签
        self.frame_label = QLabel(self)
        self.frame_label.setGeometry(0, 0, self.width(), self.height())
        self.frame_label.setStyleSheet("border: 2px dashed red;")
        self.frame_label.hide()

    def on_key_pressed(self, key):
        """线程安全方式处理按键按下事件"""
        try:
            # 只有按键在状态字典中且已启用显示时才更新状态和图片
            if key in key_states:
                key_states[key] = True
                if key in key_labels and key_labels[key].isVisible():
                    pixmap = load_image(key, True)
                    key_labels[key].setPixmap(pixmap)
        except Exception as e:
            print(f"处理按键按下时出错: {e}")

    def on_key_released(self, key):
        """线程安全方式处理按键释放事件"""
        try:
            # 只有按键在状态字典中且已启用显示时才更新状态和图片
            if key in key_states:
                key_states[key] = False
                if key in key_labels and key_labels[key].isVisible():
                    pixmap = load_image(key, False)
                    key_labels[key].setPixmap(pixmap)
        except Exception as e:
            print(f"处理按键释放时出错: {e}")

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.offset = event.pos()

    def mouseMoveEvent(self, event):
        if hasattr(self, 'offset') and self.offset is not None:
            try:
                new_pos = event.globalPos() - self.offset
                self.move(new_pos)
            except Exception as e:
                print(f"窗口移动出错: {e}")

    def enterEvent(self, event):
        self.frame_label.show()

    def leaveEvent(self, event):
        self.frame_label.hide()
        
    def apply_layout(self, new_positions):
        """应用新的布局"""
        try:
            # 更新每个按键标签的位置和大小（除了底部行）
            for key, rect in new_positions.items():
                # 排除底部行，它们由rearrange_bottom_row处理
                if key in key_labels and key not in ['alt', 'f', 'space']:
                    key_labels[key].setGeometry(rect)
            
            # 更新窗口大小
            if new_positions == compact_key_positions:
                self.resize(compact_window_size)
                if hasattr(self, 'frame_label'):
                    self.frame_label.resize(compact_window_size)
            else:
                self.resize(original_window_size)
                if hasattr(self, 'frame_label'):
                    self.frame_label.resize(original_window_size)
                    
            # 重新布局底部行（alt, f, space）
            self.rearrange_bottom_row('compact' if new_positions == compact_key_positions else 'original')
        except Exception as e:
            print(f"应用布局时出错: {e}")
            
    def toggle_key_visibility(self, key, visible):
        """切换指定按键的可见性"""
        if key in key_labels:
            key_labels[key].setVisible(visible)
            # 如果设置为不可见，则确保状态恢复为未按下
            if not visible and key in key_states:
                key_states[key] = False
            # 重新布局底部行
            self.rearrange_bottom_row('compact' if bot else 'original')
            
    def rearrange_bottom_row(self, layout_type='original'):
        """动态计算并设置底部行（alt, f, space）的位置"""
        # 确定基础位置参数
        if layout_type == 'compact':
            y = 300  # 调整为300
            key_height = 50
            space_width = 80
            # 按键宽度
            alt_width = 100
            f_width = 50
        else:
            y = 330  # 原始布局的y坐标
            key_height = 75
            space_width = 140
            # 按键宽度
            alt_width = 120
            f_width =75

        
        # 计算起始位置
        x_offset = 0  # 从最左侧顶格开始
        
        # 按顺序依次设置alt, f, space的位置
        if 'alt' in key_labels and key_labels['alt'].isVisible():
            key_labels['alt'].setGeometry(QRect(x_offset, y, alt_width, key_height))
            x_offset += alt_width + 1  # 添加1像素间距
        
        if 'f' in key_labels and key_labels['f'].isVisible():
            key_labels['f'].setGeometry(QRect(x_offset, y, f_width, key_height))
            x_offset += f_width + 1  # 添加1像素间距
        
        if 'space' in key_labels and key_labels['space'].isVisible():
            # 确保space的宽度随布局变化而变化
            key_labels['space'].setGeometry(QRect(x_offset, y, space_width, key_height))
            
        # 如果有按键隐藏，可能需要更新窗口大小（此处省略，因为窗口大小已固定）
        
        # 打印调试信息（开发时可启用）
        #print(f"重排底部行: layout_type={layout_type}, alt={key_labels['alt'].isVisible()}, f={key_labels['f'].isVisible()}, space={key_labels['space'].isVisible()}")


# 控制台
class ControlConsole(QWidget):
    def __init__(self):
        super().__init__()
        self.setMinimumSize(400, 280)  # 增加窗口高度以适应新控件
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        # 加载键盘映射配置
        self.key_mapping = self.load_key_mapping()
        
        # 初始化反向映射字典
        self.update_reverse_mapping()

        self.init_ui()
        self.resize_keys()

    def load_key_mapping(self):
        try:
            # 尝试从文件加载映射配置
            if os.path.exists("key_mapping.json"):
                with open("key_mapping.json", "r", encoding="utf-8") as f:
                    return json.load(f)
        except Exception as e:
            print(f"加载按键映射失败: {e}")
        
        # 如果加载失败，使用默认配置
        return DEFAULT_MAPPINGS.copy()


    def update_reverse_mapping(self):
        """完全重建物理按键到功能键的映射"""
        self.reverse_mapping = {}
        for func, keys in self.key_mapping.items():
            image_key = FUNCTION_TO_KEY.get(func)
            if not image_key:
                # 打印警告但不中断执行
                print(f"警告: 功能'{func}'没有对应的图片键定义")
                continue
                
            primary = keys["primary"]
            secondary = keys["secondary"]
            
            print(f"构建映射: {func} ({image_key}) -> 主键={primary}, 副键={secondary}")
            
            if primary:
                if primary in self.reverse_mapping:
                    print(f"冲突: 物理键'{primary}'已经映射到'{self.reverse_mapping[primary]}'，将被覆盖为'{image_key}'")
                self.reverse_mapping[primary] = image_key
                print(f"添加映射: {primary} -> {image_key}")
                
            if secondary:
                if secondary in self.reverse_mapping:
                    print(f"冲突: 物理键'{secondary}'已经映射到'{self.reverse_mapping[secondary]}'，将被覆盖为'{image_key}'")
                self.reverse_mapping[secondary] = image_key
                print(f"添加映射: {secondary} -> {image_key}")
                
        print("最终反向映射:")
        for phys_key, func_key in self.reverse_mapping.items():
            print(f"  {phys_key} -> {func_key}")
        print("=" * 50)

    def save_key_mapping(self):
        try:
            with open("key_mapping.json", "w", encoding="utf-8") as f:
                json.dump(self.key_mapping, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存按键映射失败: {e}")
    


    def init_ui(self):
        layout = QVBoxLayout()
    
        # 按键集合显示
        self.show_keys_checkbox = QCheckBox("开启按键集合")
        self.show_keys_checkbox.setChecked(True)  # 默认开启
        self.show_keys_checkbox.stateChanged.connect(self.toggle_keys)
        layout.addWidget(self.show_keys_checkbox)
    
        # 置顶显示
        self.always_top_checkbox = QCheckBox("置顶显示")
        self.always_top_checkbox.setChecked(True)  # 默认开启
        self.always_top_checkbox.stateChanged.connect(self.toggle_always_top)
        layout.addWidget(self.always_top_checkbox)
    
        # 键盘模式
        self.input_mode_checkbox = QCheckBox("原始输入模式")
        self.input_mode_checkbox.setChecked(True)  # 默认开启
        self.input_mode_checkbox.stateChanged.connect(self.toggle_input_mode)
        layout.addWidget(self.input_mode_checkbox)
    
        # 按键集合不透明度调整
        opacity_layout = QFormLayout()
        self.key_opacity_slider = QSlider(Qt.Horizontal)
        self.key_opacity_slider.setMinimum(10)  # 最低10%透明度
        self.key_opacity_slider.setMaximum(100)
        self.key_opacity_slider.setValue(100)
        self.key_opacity_slider.valueChanged.connect(self.adjust_key_opacity)
        opacity_layout.addRow("按键不透明度:", self.key_opacity_slider)
        layout.addLayout(opacity_layout)
    
        # 添加说明标签
        layout.addWidget(QLabel("显示/隐藏按键:"))
    
        # 创建水平布局用于三个复选框
        hbox = QHBoxLayout()
    
        # alt 是否开启显示
        self.alt_checkbox = QCheckBox("Alt键")
        self.alt_checkbox.setChecked(False)  # 默认关闭
        self.alt_checkbox.stateChanged.connect(self.handle_alt_checkbox)
        hbox.addWidget(self.alt_checkbox)
    
        # f 是否开启显示
        self.f_checkbox = QCheckBox("F键")
        self.f_checkbox.setChecked(False)  # 默认关闭
        self.f_checkbox.stateChanged.connect(self.handle_f_checkbox)
        hbox.addWidget(self.f_checkbox)
    
        # space 是否开启显示
        self.space_checkbox = QCheckBox("空格键")
        self.space_checkbox.setChecked(False)  # 默认关闭
        self.space_checkbox.stateChanged.connect(self.handle_space_checkbox)
        hbox.addWidget(self.space_checkbox)
    
        # 将水平布局添加到主垂直布局
        layout.addLayout(hbox)
        layout.addSpacing(10)  # 增加一点间距
    
        # 功能按钮
        button_layout = QFormLayout()
        
        # 自定义键位映射按钮
        self.keymap_button = QPushButton("自定义键位映射")
        self.keymap_button.clicked.connect(self.open_key_mapping_dialog)
        button_layout.addRow(self.keymap_button)  # 添加到表单布局
        
        # 缩放功能按钮
        self.resize_button = QPushButton("缩放按键布局")
        self.resize_button.clicked.connect(self.toggle_compact_mode)
        button_layout.addRow(self.resize_button)
        
        # 重启监听器按钮
        self.restart_button = QPushButton("重启监听器")
        self.restart_button.clicked.connect(self.restart_listener)
        button_layout.addRow(self.restart_button)
        
        # 退出按钮
        self.exit_button = QPushButton("退出程序")
        self.exit_button.clicked.connect(self.exit_app)
        button_layout.addRow(self.exit_button)
        
        layout.addLayout(button_layout)
        self.setLayout(layout)
    
        
    def handle_alt_checkbox(self, state):
        """处理Alt键复选框变化"""
        self.toggle_alt_key(state == Qt.Checked)
        
    def handle_f_checkbox(self, state):
        """处理F键复选框变化"""
        self.toggle_f_key(state == Qt.Checked)
        
    def handle_space_checkbox(self, state):
        """处理空格键复选框变化"""
        self.toggle_space_key(state == Qt.Checked)


    def open_key_mapping_dialog(self):
        dialog = KeyMappingDialog(self.key_mapping.copy(), self)  # 使用副本避免直接修改
        if dialog.exec_() == QDialog.Accepted:
            # 更新为对话框返回的最新映射
            self.key_mapping = dialog.mappings
            
            # 保存到文件
            self.save_key_mapping()
            
            # 完全重建反向映射
            self.update_reverse_mapping()
            
            # 强制重启监听器以应用新映射
            print("强制重启监听器...")
            self.force_restart_listener()
    
    def force_restart_listener(self):
        """强制重启监听器，确保新旧监听器切换"""
        global listener
        try:
            if listener and listener.is_alive():
                print("停止现有监听器...")
                listener.stop()
                listener.join(timeout=0.5)
                listener = None
                print("现有监听器已停止")
            
            # 根据模式重新启动监听器
            if self.input_mode_checkbox.isChecked():
                self.start_raw_listener()
            else:
                self.start_standard_listener()
        except Exception as e:
            print(f"强制重启监听器时出错: {e}")
            
    # 修改按键监听器相关方法，添加反向映射处理
    
    def original_on_press(self, key):
        # 获取原始键名
        raw_key = self.get_key_char(key)
        if not raw_key:
            return
            
        # 查找键名对应的功能键
        mapped_key = self.reverse_mapping.get(raw_key)
        
        # 调试日志
        print(f"按键按下: raw={raw_key}, mapped={mapped_key}")
        
        # 如果找到映射键名且该键在状态字典中，则触发按下事件
        if mapped_key and mapped_key in key_states:
            key_signals.key_pressed.emit(mapped_key)
        else:
            # 如果物理按键本身就在 key_states 中
            if raw_key in key_states:
                key_signals.key_pressed.emit(raw_key)
    
    def original_on_release(self, key):
        # 获取原始键名
        raw_key = self.get_key_char(key)
        if not raw_key:
            return
            
        # 查找键名对应的功能键
        mapped_key = self.reverse_mapping.get(raw_key, raw_key)
        
        # 如果找到映射键名且该键在状态字典中，则触发释放事件
        if mapped_key and mapped_key in key_states:
            key_signals.key_released.emit(mapped_key)
        else:
            # 如果物理按键本身就在 key_states 中 (如 s 键需要直接触发 down)
            if raw_key in key_states:
                key_signals.key_released.emit(raw_key)

    def get_key_char(self, key):
        """扩展支持的按键列表"""
        # 特殊按键处理
        special_keys = {
            Key.alt: 'alt',
            Key.alt_l: 'alt',
            Key.alt_r: 'alt',
            Key.space: 'space',
            Key.ctrl: 'ctrl',
            Key.ctrl_l: 'ctrl',
            Key.ctrl_r: 'ctrl',
            Key.shift: 'shift',
            Key.shift_l: 'shift',
            Key.shift_r: 'shift',
            Key.up: 'up',
            Key.down: 'down',
            Key.left: 'left',
            Key.right: 'right',
            Key.enter: 'enter',
            Key.backspace: 'backspace',
            Key.delete: 'delete',
            Key.tab: 'tab',
            Key.caps_lock: 'capslock',
            Key.insert: 'insert',
            Key.home: 'home',
            Key.end: 'end',
            Key.page_up: 'pageup',
            Key.page_down: 'pagedown',
            Key.print_screen: 'print',
            Key.scroll_lock: 'scrolllock',
            Key.pause: 'pause',
            Key.num_lock: 'numlock',
            Key.esc: 'esc',
            Key.f1: 'f1',
            Key.f2: 'f2',
            Key.f3: 'f3',
            Key.f4: 'f4',
            Key.f5: 'f5',
            Key.f6: 'f6',
            Key.f7: 'f7',
            Key.f8: 'f8',
            Key.f9: 'f9',
            Key.f10: 'f10',
            Key.f11: 'f11',
            Key.f12: 'f12',
        }
        
        # 处理特殊按键
        if key in special_keys:
            return special_keys[key]
        
        try:
            # 获取按键字符
            if hasattr(key, 'char') and key.char:
                return key.char
            
            # 处理数字键
            if hasattr(key, 'value') and key.value < 0xFE00:
                return chr(key.value).lower()
                
            # 处理小键盘按键
            numpad_keys = {
                '<96>': '0', '<97>': '1', '<98>': '2', '<99>': '3', '<100>': '4',
                '<101>': '5', '<102>': '6', '<103>': '7', '<104>': '8', '<105>': '9',
                '<110>': '.', '<107>': '+', '<109>': '-', '<106>': '*', '<111>': '/'
            }
            key_str = str(key)
            if key_str in numpad_keys:
                return numpad_keys[key_str]
                
            # 处理其他按键
            key_mapping = {
                '}': ']', '{': '[', '_': '-', '+': '=', ':': ';', 
                '"': "'", '<': ',', '>': '.', '?': '/', '|': '\\', 
                '~': '`', '!': '1', '@': '2', '#': '3', '$': '4',
                '%': '5', '^': '6', '&': '7', '*': '8', '(': '9',
                ')': '0'
            }
            
            if hasattr(key, 'name') and key.name not in ['win', 'win_l', 'win_r']:
                return key.name.lower()
            
        except AttributeError:
            pass
            
        return None



    def toggle_keys(self, state):
        # 只有当按键标签存在时才设置可见性
        try:
            # 检查全局复选框状态
            show_all = state == Qt.Checked
            
            for key, label in key_labels.items():
                # 特殊处理：alt、f、space键根据各自的复选框状态决定
                if key == 'alt':
                    visibility = show_all and self.alt_checkbox.isChecked()
                elif key == 'f':
                    visibility = show_all and self.f_checkbox.isChecked()
                elif key == 'space':
                    visibility = show_all and self.space_checkbox.isChecked()
                else:
                    visibility = show_all
                    
                label.setVisible(visibility)
                
            # 全局显示状态改变也需要重新布局
            global bot
            window.rearrange_bottom_row('compact' if bot else 'original')
        except Exception as e:
            print(f"切换按键显示时出错: {e}")

    def toggle_always_top(self, state):
        try:
            if state == Qt.Checked:
                window.setWindowFlags(window.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowTransparentForInput | Qt.Tool)
            else:
                flags = window.windowFlags()
                flags &= ~Qt.WindowStaysOnTopHint
                window.setWindowFlags(flags)
            window.show()
        except Exception as e:
            print(f"切换置顶时出错: {e}")

    def toggle_input_mode(self, state):
        if state == Qt.Checked:
            self.start_raw_listener()
        else:
            self.start_standard_listener()
            
    def toggle_alt_key(self, visible):
        """切换alt键的显示状态"""
        if hasattr(self, 'show_keys_checkbox') and self.show_keys_checkbox.isChecked():
            window.toggle_key_visibility('alt', visible)
            # rearrange_bottom_row已在toggle_key_visibility中调用

    def toggle_f_key(self, visible):
        """切换f键的显示状态"""
        if hasattr(self, 'show_keys_checkbox') and self.show_keys_checkbox.isChecked():
            window.toggle_key_visibility('f', visible)
            # rearrange_bottom_row已在toggle_key_visibility中调用

    def toggle_space_key(self, visible):
        """切换space键的显示状态"""
        if hasattr(self, 'show_keys_checkbox') and self.show_keys_checkbox.isChecked():
            window.toggle_key_visibility('space', visible)
            # rearrange_bottom_row已在toggle_key_visibility中调用

    def start_raw_listener(self):
        """启动原始输入监听器"""
        global listener
        try:
            if listener and listener.is_alive():
                listener.stop()
                listener.join(timeout=0.5)
            
            # 创建新的原始输入监听器
            listener = keyboard.Listener(
                on_press=self.on_press_safe,
                on_release=self.on_release_safe,
                win32_event_filter=self.keyboard_win32_event_filter_safe,
                suppress=False
            )
            listener.start()
            print("原始输入模式已启动")
        except Exception as e:
            print(f"原始输入模式启动失败: {e}")

    def start_standard_listener(self):
        """启动标准监听器"""
        global listener
        try:
            if listener and listener.is_alive():
                listener.stop()
                listener.join(timeout=0.5)
            
            # 创建新的标准监听器
            listener = keyboard.Listener(
                on_press=self.on_press_safe,
                on_release=self.on_release_safe
            )
            listener.start()
            print("标准输入模式已启动")
        except Exception as e:
            print(f"标准输入模式启动失败: {e}")
    
    def restart_listener(self):
        """重启监听器"""
        if self.input_mode_checkbox.isChecked():
            self.start_raw_listener()
        else:
            self.start_standard_listener()

    def adjust_key_opacity(self, value):
        opacity = value / 100.0
        # 只有当按键标签存在时才设置不透明度
        try:
            for key, label in key_labels.items():
                if label.isVisible():  # 只对可见的按键设置透明度
                    effect = QGraphicsOpacityEffect()
                    effect.setOpacity(opacity)
                    label.setGraphicsEffect(effect)
        except Exception as e:
            print(f"调整不透明度时出错: {e}")
    
    def resize_keys(self):
        """等比例缩放按键集合（按您的要求保留不变）"""
        # 恢复原始代码的功能
        for key, rect in key_positions.items():
            if key in key_labels and key not in ['alt', 'f', 'space']:  # 排除底部行
                new_rect = rect.adjusted(-10, -10, 10, 10)  # 示例缩放
                key_labels[key].setGeometry(new_rect)
        # 重新布局底部行
        window.rearrange_bottom_row('original')
    
    def toggle_compact_mode(self):
        """切换紧凑模式和原始模式"""
        global bot, key_positions
        try:
            if not bot:
                # 切换到紧凑模式（无间隔）
                window.apply_layout(compact_key_positions)
                key_positions = compact_key_positions.copy()
                self.resize_button.setText("恢复按键集合")
                bot = True
            else:
                # 恢复到原始模式（有间隔）
                window.apply_layout(original_key_positions)
                key_positions = original_key_positions.copy()
                self.resize_button.setText("缩放按键集合")
                # 调用resize_keys方法进行缩放
                self.resize_keys()
                bot = False
        except Exception as e:
            print(f"切换布局模式时出错: {e}")
            
    def exit_app(self):
        """退出应用程序"""
        global listener
        try:
            if listener and listener.is_alive():
                listener.stop()
                listener.join(timeout=0.5)
            QApplication.quit()
        except:
            # 强制退出作为最后手段
            os._exit(0)
    
    # 以下方法增加了安全性包装器
    def on_press_safe(self, key):
        """安全的按键按下处理"""
        try:
            self.original_on_press(key)
        except Exception as e:
            print(f"按键按下处理错误: {e}")

    def on_release_safe(self, key):
        """安全的按键释放处理"""
        try:
            self.original_on_release(key)
        except Exception as e:
            print(f"按键释放处理错误: {e}")

    def keyboard_win32_event_filter_safe(self, msg, data):
        """安全的Windows事件过滤器"""
        try:
            return self.keyboard_win32_event_filter(msg, data)
        except Exception as e:
            print(f"Windows事件过滤器错误: {e}")
            return True  # 继续事件处理链
    



    def keyboard_win32_event_filter(self, msg, data):
        """从原始Windows消息处理键盘事件"""
        # 扩展的键码映射（支持大多数按键）
        key_map = {
            # 字母键
            win32con.VK_A: 'a', win32con.VK_B: 'b', win32con.VK_C: 'c', 
            win32con.VK_D: 'd', win32con.VK_E: 'e', win32con.VK_F: 'f',
            win32con.VK_G: 'g', win32con.VK_H: 'h', win32con.VK_I: 'i',
            win32con.VK_J: 'j', win32con.VK_K: 'k', win32con.VK_L: 'l',
            win32con.VK_M: 'm', win32con.VK_N: 'n', win32con.VK_O: 'o',
            win32con.VK_P: 'p', win32con.VK_Q: 'q', win32con.VK_R: 'r',
            win32con.VK_S: 's', win32con.VK_T: 't', win32con.VK_U: 'u',
            win32con.VK_V: 'v', win32con.VK_W: 'w', win32con.VK_X: 'x',
            win32con.VK_Y: 'y', win32con.VK_Z: 'z',
            
            # 数字键
            win32con.VK_0: '0', win32con.VK_1: '1', win32con.VK_2: '2', 
            win32con.VK_3: '3', win32con.VK_4: '4', win32con.VK_5: '5', 
            win32con.VK_6: '6', win32con.VK_7: '7', win32con.VK_8: '8', 
            win32con.VK_9: '9',
            
            # 符号键
            win32con.VK_OEM_MINUS: '-',    # -_
            win32con.VK_OEM_PLUS: '=',     # =+
            win32con.VK_OEM_1: ';',        # ;:
            win32con.VK_OEM_2: '/',        # /?
            win32con.VK_OEM_3: '`',        # `~
            win32con.VK_OEM_4: '[',        # [{
            win32con.VK_OEM_5: '\\',       # \|
            win32con.VK_OEM_6: ']',        # ]}
            win32con.VK_OEM_7: "'",        # '"
            win32con.VK_OEM_COMMA: ',',    # ,<
            win32con.VK_OEM_PERIOD: '.',   # .>
            
            # 方向键
            win32con.VK_LEFT: 'left',
            win32con.VK_UP: 'up',
            win32con.VK_RIGHT: 'right',
            win32con.VK_DOWN: 'down',
            
            # 修饰键
            win32con.VK_SHIFT: 'shift',
            win32con.VK_LSHIFT: 'shift',    # 左Shift
            win32con.VK_RSHIFT: 'shift',    # 右Shift
            win32con.VK_CONTROL: 'ctrl',
            win32con.VK_LCONTROL: 'ctrl',   # 左Ctrl
            win32con.VK_RCONTROL: 'ctrl',   # 右Ctrl
            win32con.VK_MENU: 'alt',        # Alt键
            0xA4: 'alt',                    # 左Alt (VK_LMENU)
            0xA5: 'alt',                    # 右Alt (VK_RMENU)
            
            # 其他按键
            win32con.VK_SPACE: 'space',     # 空格
            win32con.VK_RETURN: 'enter',    # 回车
            win32con.VK_BACK: 'backspace',  # Backspace
            win32con.VK_TAB: 'tab',         # Tab
            win32con.VK_CAPITAL: 'capslock', # Caps Lock
            win32con.VK_ESCAPE: 'esc',       # Esc
            win32con.VK_SNAPSHOT: 'print',   # Print Screen
            win32con.VK_SCROLL: 'scrolllock', # Scroll Lock
            win32con.VK_PAUSE: 'pause',       # Pause/Break
            win32con.VK_INSERT: 'insert',     # Insert
            win32con.VK_HOME: 'home',         # Home
            win32con.VK_PRIOR: 'pageup',      # Page Up
            win32con.VK_NEXT: 'pagedown',     # Page Down
            win32con.VK_DELETE: 'delete',     # Delete
            win32con.VK_END: 'end',           # End
            
            # 小键盘
            win32con.VK_NUMLOCK: 'numlock',   # Num Lock
            win32con.VK_NUMPAD0: '0',        # 小键盘0
            win32con.VK_NUMPAD1: '1',
            win32con.VK_NUMPAD2: '2',
            win32con.VK_NUMPAD3: '3',
            win32con.VK_NUMPAD4: '4',
            win32con.VK_NUMPAD5: '5',
            win32con.VK_NUMPAD6: '6',
            win32con.VK_NUMPAD7: '7',
            win32con.VK_NUMPAD8: '8',
            win32con.VK_NUMPAD9: '9',
            win32con.VK_DIVIDE: '/',         # 小键盘/
            win32con.VK_MULTIPLY: '*',       # 小键盘*
            win32con.VK_ADD: '+',            # 小键盘+
            win32con.VK_SUBTRACT: '-',       # 小键盘-
            win32con.VK_DECIMAL: '.',        # 小键盘.
            win32con.VK_SEPARATOR: ',',      # 小键盘,
            
            # 功能键
            win32con.VK_F1: 'f1',
            win32con.VK_F2: 'f2',
            win32con.VK_F3: 'f3',
            win32con.VK_F4: 'f4',
            win32con.VK_F5: 'f5',
            win32con.VK_F6: 'f6',
            win32con.VK_F7: 'f7',
            win32con.VK_F8: 'f8',
            win32con.VK_F9: 'f9',
            win32con.VK_F10: 'f10',
            win32con.VK_F11: 'f11',
            win32con.VK_F12: 'f12',
        }
        
        vk_code = data.vkCode
        
        if msg in (win32con.WM_KEYDOWN, win32con.WM_SYSKEYDOWN):
            key = key_map.get(vk_code)
            if key:
                # 应用映射 - 查找对应的功能键
                mapped_key = self.reverse_mapping.get(key)
                
                # 处理映射键
                if mapped_key and mapped_key in key_states:
                    key_signals.key_pressed.emit(mapped_key)
                else:
                    # 如果物理按键本身就在 key_states 中
                    if key in key_states:
                        key_signals.key_pressed.emit(key)
        
        elif msg in (win32con.WM_KEYUP, win32con.WM_SYSKEYUP):
            key = key_map.get(vk_code)
            if key:
                # 应用映射 - 查找对应的功能键
                mapped_key = self.reverse_mapping.get(key)
                
                # 处理映射键
                if mapped_key and mapped_key in key_states:
                    key_signals.key_released.emit(mapped_key)
                else:
                    # 如果物理按键本身就在 key_states 中
                    if key in key_states:
                        key_signals.key_released.emit(key)
        
        # 继续事件处理链
        return True

# 全局键盘监听器
listener = None

# 异常处理钩子
def excepthook(exctype, value, tb):
    """处理未捕获的异常"""
    error_msg = ''.join(traceback.format_exception(exctype, value, tb))
    print(f"未捕获的异常:\n{error_msg}")
    
    # 尝试重启监听器
    if hasattr(app, 'restartListener'):
        try:
            app.restartListener()
            print("成功重启监听器")
        except Exception as e:
            print(f"重启监听器失败: {e}")

# 主程序
if __name__ == "__main__":
    # 设置异常钩子
    sys.excepthook = excepthook
    
    # 在Windows上提升权限
    if sys.platform == 'win32':
        if not run_as_admin():
            print("注意：程序未以管理员权限运行，某些功能可能受限")
    
    app = None
    try:
        app = QApplication(sys.argv)
        app.setStyle("Fusion")  # 使用更现代的UI风格
        
        # 设置应用图标
        icon_path = os.path.join(RESOURCE_DIR, "KOBATO.png")
        if os.path.exists(icon_path):
            app.setWindowIcon(QIcon(icon_path))
        else:
            print(f"警告: 图标文件 '{icon_path}' 不存在")
        
        # 检查资源文件夹是否存在
        if not os.path.exists(RESOURCE_DIR):
            print(f"警告: 资源文件夹 '{RESOURCE_DIR}' 不存在。按键图片将无法显示。")
            os.makedirs(RESOURCE_DIR, exist_ok=True)
            print(f"已创建资源文件夹: '{RESOURCE_DIR}'，请添加图片资源")
        
        # 创建浮动窗口
        window = FloatingWindow()
        window.setWindowTitle("KOBATO飞车按键显示v1.3")
        window.show()
        
        # 创建控制台
        console = ControlConsole()
        console.setWindowTitle("KOBATO控制台")
        console.setWindowOpacity(0.8)  # 添加半透明效果减少干扰
        console.show()
        
        # 添加重启监听器方法
        setattr(app, 'restartListener', console.restart_listener)
        
        # 初始创建监听器
        console.start_standard_listener()
        
        # 初始化时重新布局底部行
        window.rearrange_bottom_row('original')
        
        sys.exit(app.exec_())
    except Exception as e:
        print(f"应用程序启动失败: {e}")
        traceback.print_exc()
        if app:
            QApplication.quit()
        sys.exit(1)
